package com.filter;

import com.utils.JwtUtil;
import com.utils.RedisUtil;
import com.utils.ThreadLocalUtil;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtAuthFilter extends OncePerRequestFilter {

    private final RedisUtil redisUtil;
    private final JwtUtil jwtUtil;

    public JwtAuthFilter(RedisUtil redisUtil, JwtUtil jwtUtil) {
        this.redisUtil = redisUtil;
        this.jwtUtil = jwtUtil;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {

        ThreadLocalUtil.remove();

        String requestPath = request.getServletPath();
        String requestMethod = request.getMethod();

        if (isPublicApi(requestPath, requestMethod)) {
            filterChain.doFilter(request, response);
            return;
        }

        if (requestPath.equals("/ws/info")) {
            String token = request.getParameter("token");
            if (token == null || !jwtUtil.validateToken(token)) {
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.setContentType("application/json");
                response.getWriter().write("{\"code\":401,\"message\":\"Token无效或缺失\"}");
                return;
            }
            response.setContentType("application/json");
            response.getWriter().write(
                    "{\"entropy\":" + System.currentTimeMillis() +
                            ",\"origins\":[\"*:*\"],\"cookie_needed\":false,\"websocket\":true}"
            );
            return;
        }

        String authHeader = request.getHeader("Authorization");
        String token = null;
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            token = authHeader.substring(7);
        }

        if (token == null) {
            sendError(response, HttpServletResponse.SC_UNAUTHORIZED, "缺少有效的Authorization头");
            return;
        }

        try {
            if (!jwtUtil.validateToken(token)) {
                sendError(response, HttpServletResponse.SC_UNAUTHORIZED, "无效的Token");
                return;
            }

            String username = jwtUtil.getUsernameFromToken(token);
            Long userId = jwtUtil.getUserIdFromToken(token);

            String currentValidToken = redisUtil.getUserToken(userId);

            if (currentValidToken == null) {
                sendError(response, HttpServletResponse.SC_UNAUTHORIZED, "Token已失效");
                return;
            }

            if (!currentValidToken.equals(token)) {
                sendError(response, HttpServletResponse.SC_UNAUTHORIZED, "账号已在其他设备登录，请重新登录");
                return;
            }

            Map<String, Object> userInfo = new HashMap<>();
            userInfo.put("id", userId);
            userInfo.put("username", username);
            userInfo.put("token", token);

            ThreadLocalUtil.set(userInfo);

            UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken(
                            userId.toString(),
                            null,
                            Collections.emptyList()
                    );
            authentication.setDetails(userId);
            SecurityContextHolder.getContext().setAuthentication(authentication);

            filterChain.doFilter(request, response);

        } catch (Exception e) {
            sendError(response, HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
        } finally {
            ThreadLocalUtil.remove();
        }
    }

    private boolean isPublicApi(String path, String method) {
        return path.equals("/api/users/register") ||
                path.equals("/api/users/login") ||
                path.startsWith("/api/users/login-by-code/") ||
                path.startsWith("/api/users/check-username") ||
                path.startsWith("/api/users/check-email") ||
                path.startsWith("/api/notes/recent-public") ||
                path.startsWith("/api/notes/public-by-tag") ||
                path.matches("/api/notes/user/\\d+/public") ||
                path.startsWith("/promot/") ||
                path.startsWith("/carouse") ||
                path.startsWith("/ws/") ||
                (path.startsWith("/api/comments/note/") && method.equals("GET")) ||
                (path.startsWith("/api/comments/reply/") && method.equals("GET"));
    }

    private void sendError(HttpServletResponse response, int status, String message) throws IOException {
        response.setStatus(status);
        response.setContentType("application/json");
        response.getWriter().write(String.format("{\"code\":%d,\"message\":\"%s\"}", status, message));
    }
}